/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- *//* vim: set ts=8 sts=2 et sw=2 tw=80: *//* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"nsContentUtils.h"#include"nsIDocument.h"#include"mozilla/Sprintf.h"#include"nsGlobalWindow.h"#include"mozilla/dom/ScriptSettings.h"#include"mozilla/DOMEventTargetHelper.h"#include"mozilla/EventDispatcher.h"#include"mozilla/EventListenerManager.h"#include"mozilla/Likely.h"namespacemozilla{usingnamespacedom;NS_IMPL_CYCLE_COLLECTION_CLASS(DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPERNS_IMPL_CYCLE_COLLECTION_TRACE_ENDNS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(DOMEventTargetHelper)if(MOZ_UNLIKELY(cb.WantDebugInfo())){charname[512];nsAutoStringuri;if(tmp->mOwnerWindow&&tmp->mOwnerWindow->GetExtantDoc()){Unused<<tmp->mOwnerWindow->GetExtantDoc()->GetDocumentURI(uri);}nsXPCOMCycleCollectionParticipant*participant=nullptr;CallQueryInterface(tmp,&participant);SprintfLiteral(name,"%s %s",participant->ClassName(),NS_ConvertUTF16toUTF8(uri).get());cb.DescribeRefCountedNode(tmp->mRefCnt.get(),name);}else{NS_IMPL_CYCLE_COLLECTION_DESCRIBE(DOMEventTargetHelper,tmp->mRefCnt.get())}NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)NS_IMPL_CYCLE_COLLECTION_TRAVERSE_ENDNS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPERNS_IMPL_CYCLE_COLLECTION_UNLINK(mListenerManager)tmp->MaybeDontKeepAlive();NS_IMPL_CYCLE_COLLECTION_UNLINK_ENDNS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(DOMEventTargetHelper)boolhasLiveWrapper=tmp->HasKnownLiveWrapper();if(hasLiveWrapper||tmp->IsCertainlyAliveForCC()){if(tmp->mListenerManager){tmp->mListenerManager->MarkForCC();}if(!hasLiveWrapper&&tmp->PreservingWrapper()){tmp->MarkWrapperLive();}returntrue;}NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_ENDNS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(DOMEventTargetHelper)returntmp->HasKnownLiveWrapperAndDoesNotNeedTracing(tmp);NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_ENDNS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(DOMEventTargetHelper)returntmp->HasKnownLiveWrapper();NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_ENDNS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMEventTargetHelper)NS_WRAPPERCACHE_INTERFACE_MAP_ENTRYNS_INTERFACE_MAP_ENTRY(nsISupports)NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)NS_INTERFACE_MAP_ENTRY(dom::EventTarget)NS_INTERFACE_MAP_ENTRY(DOMEventTargetHelper)NS_INTERFACE_MAP_ENDNS_IMPL_CYCLE_COLLECTING_ADDREF(DOMEventTargetHelper)NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(DOMEventTargetHelper,LastRelease())NS_IMPL_DOMTARGET_DEFAULTS(DOMEventTargetHelper)DOMEventTargetHelper::~DOMEventTargetHelper(){if(nsPIDOMWindowInner*owner=GetOwner()){nsGlobalWindow::Cast(owner)->RemoveEventTargetObject(this);}if(mListenerManager){mListenerManager->Disconnect();}ReleaseWrapper(this);}voidDOMEventTargetHelper::BindToOwner(nsPIDOMWindowInner*aOwner){nsCOMPtr<nsIGlobalObject>glob=do_QueryInterface(aOwner);BindToOwner(glob);}voidDOMEventTargetHelper::BindToOwner(nsIGlobalObject*aOwner){nsCOMPtr<nsIGlobalObject>parentObject=do_QueryReferent(mParentObject);if(parentObject){if(mOwnerWindow){nsGlobalWindow::Cast(mOwnerWindow)->RemoveEventTargetObject(this);mOwnerWindow=nullptr;}mParentObject=nullptr;mHasOrHasHadOwnerWindow=false;}if(aOwner){mParentObject=do_GetWeakReference(aOwner);MOZ_ASSERT(mParentObject,"All nsIGlobalObjects must support nsISupportsWeakReference");// Let's cache the result of this QI for fast access and off main thread usagemOwnerWindow=nsCOMPtr<nsPIDOMWindowInner>(do_QueryInterface(aOwner)).get();if(mOwnerWindow){mHasOrHasHadOwnerWindow=true;nsGlobalWindow::Cast(mOwnerWindow)->AddEventTargetObject(this);}}}voidDOMEventTargetHelper::BindToOwner(DOMEventTargetHelper*aOther){if(mOwnerWindow){nsGlobalWindow::Cast(mOwnerWindow)->RemoveEventTargetObject(this);mOwnerWindow=nullptr;mParentObject=nullptr;mHasOrHasHadOwnerWindow=false;}if(aOther){mHasOrHasHadOwnerWindow=aOther->HasOrHasHadOwner();if(aOther->GetParentObject()){mParentObject=do_GetWeakReference(aOther->GetParentObject());MOZ_ASSERT(mParentObject,"All nsIGlobalObjects must support nsISupportsWeakReference");// Let's cache the result of this QI for fast access and off main thread usagemOwnerWindow=nsCOMPtr<nsPIDOMWindowInner>(do_QueryInterface(aOther->GetParentObject())).get();if(mOwnerWindow){mHasOrHasHadOwnerWindow=true;nsGlobalWindow::Cast(mOwnerWindow)->AddEventTargetObject(this);}}}}voidDOMEventTargetHelper::DisconnectFromOwner(){mOwnerWindow=nullptr;mParentObject=nullptr;// Event listeners can't be handled anymore, so we can release them here.if(mListenerManager){mListenerManager->Disconnect();mListenerManager=nullptr;}MaybeDontKeepAlive();}nsPIDOMWindowInner*DOMEventTargetHelper::GetWindowIfCurrent()const{if(NS_FAILED(CheckInnerWindowCorrectness())){returnnullptr;}returnGetOwner();}nsIDocument*DOMEventTargetHelper::GetDocumentIfCurrent()const{nsPIDOMWindowInner*win=GetWindowIfCurrent();if(!win){returnnullptr;}returnwin->GetDoc();}NS_IMETHODIMPDOMEventTargetHelper::RemoveEventListener(constnsAString&aType,nsIDOMEventListener*aListener,boolaUseCapture){EventListenerManager*elm=GetExistingListenerManager();if(elm){elm->RemoveEventListener(aType,aListener,aUseCapture);}returnNS_OK;}NS_IMPL_REMOVE_SYSTEM_EVENT_LISTENER(DOMEventTargetHelper)NS_IMETHODIMPDOMEventTargetHelper::AddEventListener(constnsAString&aType,nsIDOMEventListener*aListener,boolaUseCapture,boolaWantsUntrusted,uint8_taOptionalArgc){NS_ASSERTION(!aWantsUntrusted||aOptionalArgc>1,"Won't check if this is chrome, you want to set ""aWantsUntrusted to false or make the aWantsUntrusted ""explicit by making aOptionalArgc non-zero.");if(aOptionalArgc<2){nsresultrv=WantsUntrusted(&aWantsUntrusted);NS_ENSURE_SUCCESS(rv,rv);}EventListenerManager*elm=GetOrCreateListenerManager();NS_ENSURE_STATE(elm);elm->AddEventListener(aType,aListener,aUseCapture,aWantsUntrusted);returnNS_OK;}voidDOMEventTargetHelper::AddEventListener(constnsAString&aType,EventListener*aListener,constAddEventListenerOptionsOrBoolean&aOptions,constNullable<bool>&aWantsUntrusted,ErrorResult&aRv){boolwantsUntrusted;if(aWantsUntrusted.IsNull()){nsresultrv=WantsUntrusted(&wantsUntrusted);if(NS_FAILED(rv)){aRv.Throw(rv);return;}}else{wantsUntrusted=aWantsUntrusted.Value();}EventListenerManager*elm=GetOrCreateListenerManager();if(!elm){aRv.Throw(NS_ERROR_UNEXPECTED);return;}elm->AddEventListener(aType,aListener,aOptions,wantsUntrusted);}NS_IMETHODIMPDOMEventTargetHelper::AddSystemEventListener(constnsAString&aType,nsIDOMEventListener*aListener,boolaUseCapture,boolaWantsUntrusted,uint8_taOptionalArgc){NS_ASSERTION(!aWantsUntrusted||aOptionalArgc>1,"Won't check if this is chrome, you want to set ""aWantsUntrusted to false or make the aWantsUntrusted ""explicit by making aOptionalArgc non-zero.");if(aOptionalArgc<2){nsresultrv=WantsUntrusted(&aWantsUntrusted);NS_ENSURE_SUCCESS(rv,rv);}returnNS_AddSystemEventListener(this,aType,aListener,aUseCapture,aWantsUntrusted);}NS_IMETHODIMPDOMEventTargetHelper::DispatchEvent(nsIDOMEvent*aEvent,bool*aRetVal){nsEventStatusstatus=nsEventStatus_eIgnore;nsresultrv=EventDispatcher::DispatchDOMEvent(this,nullptr,aEvent,nullptr,&status);*aRetVal=(status!=nsEventStatus_eConsumeNoDefault);returnrv;}nsresultDOMEventTargetHelper::DispatchTrustedEvent(constnsAString&aEventName){RefPtr<Event>event=NS_NewDOMEvent(this,nullptr,nullptr);event->InitEvent(aEventName,false,false);returnDispatchTrustedEvent(event);}nsresultDOMEventTargetHelper::DispatchTrustedEvent(nsIDOMEvent*event){event->SetTrusted(true);booldummy;returnDispatchEvent(event,&dummy);}nsresultDOMEventTargetHelper::SetEventHandler(nsIAtom*aType,JSContext*aCx,constJS::Value&aValue){RefPtr<EventHandlerNonNull>handler;JS::Rooted<JSObject*>callable(aCx);if(aValue.isObject()&&JS::IsCallable(callable=&aValue.toObject())){handler=newEventHandlerNonNull(aCx,callable,dom::GetIncumbentGlobal());}SetEventHandler(aType,EmptyString(),handler);returnNS_OK;}voidDOMEventTargetHelper::GetEventHandler(nsIAtom*aType,JSContext*aCx,JS::Value*aValue){EventHandlerNonNull*handler=GetEventHandler(aType,EmptyString());if(handler){*aValue=JS::ObjectOrNullValue(handler->CallableOrNull());}else{*aValue=JS::NullValue();}}nsresultDOMEventTargetHelper::GetEventTargetParent(EventChainPreVisitor&aVisitor){aVisitor.mCanHandle=true;aVisitor.mParentTarget=nullptr;returnNS_OK;}nsresultDOMEventTargetHelper::PostHandleEvent(EventChainPostVisitor&aVisitor){returnNS_OK;}nsresultDOMEventTargetHelper::DispatchDOMEvent(WidgetEvent*aEvent,nsIDOMEvent*aDOMEvent,nsPresContext*aPresContext,nsEventStatus*aEventStatus){returnEventDispatcher::DispatchDOMEvent(this,aEvent,aDOMEvent,aPresContext,aEventStatus);}EventListenerManager*DOMEventTargetHelper::GetOrCreateListenerManager(){if(!mListenerManager){mListenerManager=newEventListenerManager(this);}returnmListenerManager;}EventListenerManager*DOMEventTargetHelper::GetExistingListenerManager()const{returnmListenerManager;}nsIScriptContext*DOMEventTargetHelper::GetContextForEventHandlers(nsresult*aRv){*aRv=CheckInnerWindowCorrectness();if(NS_FAILED(*aRv)){returnnullptr;}nsPIDOMWindowInner*owner=GetOwner();returnowner?nsGlobalWindow::Cast(owner)->GetContextInternal():nullptr;}nsresultDOMEventTargetHelper::WantsUntrusted(bool*aRetVal){nsresultrv=CheckInnerWindowCorrectness();NS_ENSURE_SUCCESS(rv,rv);nsCOMPtr<nsIDocument>doc=GetDocumentIfCurrent();// We can let listeners on workers to always handle all the events.*aRetVal=(doc&&!nsContentUtils::IsChromeDoc(doc))||!NS_IsMainThread();returnrv;}voidDOMEventTargetHelper::EventListenerAdded(nsIAtom*aType){IgnoredErrorResultrv;EventListenerWasAdded(Substring(nsDependentAtomString(aType),2),rv);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::EventListenerAdded(constnsAString&aType){IgnoredErrorResultrv;EventListenerWasAdded(aType,rv);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::EventListenerRemoved(nsIAtom*aType){IgnoredErrorResultrv;EventListenerWasRemoved(Substring(nsDependentAtomString(aType),2),rv);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::EventListenerRemoved(constnsAString&aType){IgnoredErrorResultrv;EventListenerWasRemoved(aType,rv);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::KeepAliveIfHasListenersFor(constnsAString&aType){mKeepingAliveTypes.mStrings.AppendElement(aType);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::KeepAliveIfHasListenersFor(nsIAtom*aType){mKeepingAliveTypes.mAtoms.AppendElement(aType);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::IgnoreKeepAliveIfHasListenersFor(constnsAString&aType){mKeepingAliveTypes.mStrings.RemoveElement(aType);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::IgnoreKeepAliveIfHasListenersFor(nsIAtom*aType){mKeepingAliveTypes.mAtoms.RemoveElement(aType);MaybeUpdateKeepAlive();}voidDOMEventTargetHelper::MaybeUpdateKeepAlive(){boolshouldBeKeptAlive=false;if(!mKeepingAliveTypes.mAtoms.IsEmpty()){for(uint32_ti=0;i<mKeepingAliveTypes.mAtoms.Length();++i){if(HasListenersFor(mKeepingAliveTypes.mAtoms[i])){shouldBeKeptAlive=true;break;}}}if(!shouldBeKeptAlive&&!mKeepingAliveTypes.mStrings.IsEmpty()){for(uint32_ti=0;i<mKeepingAliveTypes.mStrings.Length();++i){if(HasListenersFor(mKeepingAliveTypes.mStrings[i])){shouldBeKeptAlive=true;break;}}}if(shouldBeKeptAlive==mIsKeptAlive){return;}mIsKeptAlive=shouldBeKeptAlive;if(mIsKeptAlive){AddRef();}else{Release();}}voidDOMEventTargetHelper::MaybeDontKeepAlive(){if(mIsKeptAlive){mIsKeptAlive=false;Release();}}}// namespace mozilla